<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Title</title>
  </head>
  
  <body>
    <div id="app">
      <form>
        用户名：<input v-model="user.name" type="text"><br>
        密码：<input v-model="user.pwd" type="password"><br>
        <br>
        
        <div style="color: red">用户信息对象更新：{{message.objUpdate}}</div>
        <div style="color: red">用户信息更新：{{message.user}}</div>
        <div style="color: red">用户名更新：{{message.name}}</div>
      </form>
    </div>
  </body>
  
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  
  <script>
    Vue.createApp({
      data() {
        return {
          message: {
            objUpdate: '',
            user: '',
            name: ''
          },
          user: {
            name: '',
            pwd: ''
          },
          watchIns: null
        };
      },
      watch: {
        // 简单监听器
        user(newVal, oldVal) {// user属性变化此处监听不到，只有user对象变化时才可监听到
          this.message.user = `旧（${oldVal}），新（${newVal}）`;
        },
        // .分割路径监听器，不能使用表达式
        'user.name'(newVal, oldVal) {
          this.message.name = `旧（${oldVal}），新（${newVal}）`;
        },
        // 监听器配置项
        user: {
          // 深层监听，默认false。遍历对象所有属性对大数据结构开销大，当需要监听对象的所以属性时使用，否则优先使用.分割路径监听器监听需要的属性
          deep: true, // user属性变化和user对象变化此处可监听到
          // 立即执行，默认false。执行时机在created，此时data、methods和computed已被处理
          immediate: true,
          // Vue组件更新之后被调用，默认在Vue组件更新之前被调用，即默认访问的DOM是未更新的
          flush: 'post',
          handler(newVal, oldVal) {// 执行函数。user属性变化但user对象未变化时newVal和oldVal相同
            this.message.objUpdate = oldVal !== newVal;
            this.message.user = `旧（${oldVal?.user}-${oldVal?.pwd}），新（${newVal?.name}-${newVal?.pwd}）`;
          },
        }
        // async与await等异步操作可用于watch内
      },
      methods: {
        createWatch() {
          // 命令式创建侦听器
          this.watchIns = this.$watch('user', (newVal, oldVal) => {
            // ...
          });
        },
        stopWatch() {
          // 手动停止侦听器，默认会在宿主组件被卸载时自动停止
          this.watchIns();
        }
      }
    }).mount('#app');
  </script>
</html>